/*  8 maggio 2012
**  Classe: Irc
**  Gestisce la comunicazione con il server e la chat IRC.
**  invia messaggi provenienti da Skype sul canale IRC e riceve tutte le comunicazioni
**  dal canale IRC inviandole su Skype
*/

#include <iostream>
#include <string>
using namespace std;

//Librerie per l'uso delle socket
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
//Libreria di gestione delle stringhe
#include <string.h>
//Librerie del SO per IO asincrono
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>

extern int sock_desc;

#include "Irc.hpp"


//La funziona viene chiamata quando si presenta una messaggio da IRC
void Read (  int sig , siginfo_t *siginfo, void *context  ) {
    //DOVREI TROVARE QUEL CAZZO DI FILE DESCRIPTOR IN
    //siginfo->si_fd
    //MA NON C'E' PD!!!
    /*
    //Recupero il socket descriptor
    int sd = sock_desc;
    char irc_r_buffer[512];
    if( read (sd, irc_r_buffer, 512) < 0 )     //Verifico che la read non fallisca
        cerr << "[IRC-ERROR] Errore di ricezione socket";
    //Se ricevo un PING da parte del server, gli rispondo PONG e fine
    if (strstr (irc_r_buffer, "PING")) {
        string pong = "PONG :";
        //pong += descr.server;
        pong += "\r\n";
        send (sd, pong.c_str() , pong.length() , 0 );
    } else {
        //Ho ricevuto un messaggio da IRC!
        //richiamo il buffer per elaborare il messaggio
        cout << irc_r_buffer << endl;       //al momento scrivo solo il messaggio in output
    }*/
}


//Costruttore

//Effettua la connessione al server IRC (registrazione utente, ecc.) ed entra
//nel canale interessato.
Irc :: Irc ( Irc_d des ) : MAX_PACK(512), descr(des) {
    //Variabili per la connessione
    struct sockaddr_in client;
    struct hostent *hp;
    int x;
    char fuffa[MAX_PACK];
    
    //Risoluzione dell'indirizzo del server
    hp = gethostbyname (descr.server.c_str()); 
    //Azzero l'area di memoria della socket
    bzero (&client, sizeof (client));     //Non funge in Windows in Linux mi da errore di scope
    client.sin_family = AF_INET;
    client.sin_port = htons (descr.port);
    client.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr;  // :|
    //Creo la socket
    sd = socket (AF_INET, SOCK_STREAM, 0);
    if (sd == 0)
        cerr << "[IRC-ERROR] Create socket fallita socket()" << endl;
    //Chiamate a sistema per l'IO asincrono
    //------------------------------------------------------------------------//
    //Modifico il sistema per le chiamate alla funzione Read
    fcntl(sd,F_SETFL,FASYNC);           //Rendo la socket asincrona
    //Aggiungo alla lista dei segnali asincroni la lettura del sd in modo asincrono
    //richiamando la funzione Read();
    struct sigaction new_action;
    //Setto la nuova azione
    new_action.sa_sigaction   = &Read;
    sigemptyset(&new_action.sa_mask);
    new_action.sa_flags = SA_SIGINFO;
    //Aggiungo alla lista delle azioni della ricezione del socket
    sigaction(SIGIO,&new_action,NULL);
    //------------------------------------------------------------------------//
    if (x = connect (sd, (struct sockaddr *) &client, sizeof (client)), x < 0)
        cerr << "[IRC-ERROR] La connect Ãš fallita!" << endl;
    //Imposto la possibilità della gestione degli eventi
    //------------------------------------------------------------------------//
    fcntl(sd,F_SETOWN,getpid());
    //------------------------------------------------------------------------//
    
    
    //Autenticazione al server:
    //$nickname = nome utilizzato all'interno di IRC
    //$username = username della mail
    //$dominio  = dominio della mail
    //$server   = server IRC al quale ci si connette
    //$name     = nome reale dell'utente
    //"NICK $nickname\r\n"
    //"USER $username $dominio $server :$name\r\n"
    //Pacchettizzo i due comani separatamente in quanto sia il nickname che il server
    //hanno una lunghezza massima di 256 byte (anche se irraggiungibili) e la loro
    //somma darebbe problemi
    string auth, auth2;
    auth = "NICK ";
    auth += descr.user.substr(0,256);     //Mi assicuro che lo username non sia maggiore di 256 byte
    auth += "\r\n";
    //Autentico il programma con le sue informazioni (non modificabili)
    auth2 = "USER ";
    auth2 += " skype2irc gmail.com ";
    auth2 += descr.server.substr(0,256);   //Idem per il server (256 byte)
    auth2 += " :Skype2IRC++\r\n";
    //Invio le prime informazioni di autenticazione
    send ( sd , auth.c_str() , auth.length() , 0 );
    send ( sd , auth2.c_str() , auth2.length() , 0 );
    
    // ATTENZIONE!!! IO SINCRONO!!!
    //------------------------------------------------------------------------//
    //Ricevo il messaggio di benvenuto dal server 
    cout << "[IRC-SERVER] ";
    do {
        if (read (sd, fuffa, MAX_PACK ) < 0) {      //Ambiente linux
            cerr << "[IRC-ERROR] problem in readl irc motd" << endl;
            //break;   //Non sempre la ricezione nulla equivale ad
                       //errore della socket, ma ad un timeout
        }
        cout << fuffa;  //Inutile visualizzare il messaggio di benvenuto
    } while (strstr (fuffa, "End of /MOTD") == NULL);
    //------------------------------------------------------------------------//
    cout << "Connesso!" << endl;
    //Prima ricevo il messaggio di benvenuto e poi imposto l'IO asincrono
    
    
    //Eseguo l'autenticazione dello username sul server
    // "PRIVMSG NICKSERV :IDENTIFY $password \r\n"
    auth = "PRIVMSG NICKSERV :IDENTIFY ";
    auth += descr.pswd.substr(0,256);
    auth += "\r\n";
    send ( sd , auth.c_str() , auth.length() , 0 );
    //Entro nel canale predefinito
    // "JOIN $channel\r\n"
    auth2 = "JOIN ";
    auth2 += descr.channel.substr(0,256);
    auth2 += "\r\n";
    send ( sd , auth2.c_str() , auth2.length() , 0 );
    //PORCATA
    sock_desc = sd;
}


//Prende un mittente e un messaggio, pacchettizza tutto e invia sul canale IRC
void Irc :: Send ( string from , string msg ) {
    string preambolo_irc;
    
    //Messaggio da Skype a IRC
    //"PRIVMSG $channel : [$from] $msg" di lunghezza massima 512 byte
    //nel caso che $msg ecceda la dimensione massima di 512 byte, il preambolo
    //viene ripetuto con l'indicazione del mittente
    
    //Genero il preambolo
    preambolo_irc = "PRIVMSG ";
    preambolo_irc += descr.channel;
    preambolo_irc += " : [";
    preambolo_irc += from;
    preambolo_irc += "] ";
    
    //Genero la lunghezza del messaggio
    int len_pacchetto = MAX_PACK - preambolo_irc.length() - 2;      //-2 i caratteri di terminazione
    
    //Ciclo l'invio del messaggio eventualmente frammentato in pacchetti da 512 byte
    for (int i = 0; i < msg.length(); i += len_pacchetto ) {       
        string pack;
        unsigned len_msg = MAX_PACK - preambolo_irc.length() - 2;
        pack = preambolo_irc;
        pack += msg.substr(i , len_msg);
        pack += "\r\n";
        
        send (sd, pack.c_str(), pack.length() , 0);  // senza il +1 che non fa parte della stringa!
        sleep (0.1);              // no flood options :D 100ms
    }
}
